﻿using FaceRecognition.Model;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using OpenCvSharp.Face;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Windows.Forms;

namespace FaceRecognition
{
    public partial class FrmRecognition : Form
    {
        public FrmRecognition()
        {
            InitializeComponent();
        }

        private List<ImageInfo> mAllInfo = null;
        private FisherFaceRecognizer mFaceRecognizer = null;


        private static string xmlPath = Application.StartupPath + "\\haarcascade_frontalface_alt.xml";
        /// <summary>
        /// 人脸识别需要获取到OpenCVSharp下的配置文件
        /// </summary>
        private CascadeClassifier cascade = new CascadeClassifier(xmlPath);
        /// <summary>
        /// 摄像头捕获的每一帧图片
        /// </summary>
        private Bitmap imgShow;
        /// <summary>
        /// 画笔工具放在全局下，方便直接使用
        /// </summary>
        private Pen pen = new Pen(Color.Red, 2);
        /// <summary>
        /// 没一张图片上识别到的人脸位置和对应的人名
        /// </summary>
        private Hashtable mDriverName = new Hashtable();
        /// <summary>
        /// 识别过程用线程单独执行
        /// </summary>
        private Thread mRecognitionThread = null;

        public FrmRecognition(List<ImageInfo> info, FisherFaceRecognizer faceRecogn)
        {
            InitializeComponent();
            this.mAllInfo = info;
            this.mFaceRecognizer = faceRecogn;
        }


        private void FrmRecognition_Load(object sender, EventArgs e)
        {
            this.pictureBox1.Paint += PictureBox1_Paint;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            mRecognitionThread = new Thread(ShowUSBCamare);
            mRecognitionThread.Name = "recognitionThread";
            mRecognitionThread.Start();
        }

        /// <summary>
        /// 显示摄像头捕捉人脸并作对比，识别人脸信息
        /// </summary>
        private void ShowUSBCamare()
        {
            Mat src = new Mat();
            FrameSource frame = Cv2.CreateFrameSource_Camera(0);
            Hashtable allMats = null;
            while (true)
            {
                frame.NextFrame(src);
                Rect[] faces = CheckFace(src);      //捕捉到所有的人脸信息
                mDriverName.Clear();
                if (faces != null && faces.Length > 0)
                {
                    allMats = CheckFace(src, faces);    //拿到所有的人脸位置和检测到的人脸Mat
                    if (allMats != null && allMats.Count > 0)
                        mDriverName = PredictFace(allMats);     //对比识别人脸信息并获取到人脸对应的人员名称
                }
                Bitmap bitmap = BitmapConverter.ToBitmap(src);
                pictureBox1.Invalidate();
                imgShow = bitmap;

                this.pictureBox1.Tag = mDriverName.Clone();
            }
        }

        private void PictureBox1_Paint(object sender, PaintEventArgs e)
        {
            if (this.imgShow == null)
                return;
            e.Graphics.DrawImage(imgShow, 0, 0);
            if (this.pictureBox1.Tag == null)
                return;
            Hashtable faces = this.pictureBox1.Tag as Hashtable;
            if (faces == null || faces.Count < 1)
                return;

            foreach(string item in faces.Keys)
            {
                Rect rect = (Rect)faces[item];
                e.Graphics.DrawRectangle(pen, new Rectangle(rect.X, rect.Y, rect.Width, rect.Height));
                e.Graphics.DrawString(item, new Font("宋体", 13, FontStyle.Regular), Brushes.Red, new PointF(rect.X, rect.Y - 16));
            }


            //e.Graphics.DrawRectangle(pen,)
        }

        /// <summary>
        /// 人脸检测
        /// </summary>
        private Rect[] CheckFace(Mat grayImage)
        {
            try
            {
                Rect[] faces = cascade.DetectMultiScale(
                    image: grayImage,
                    scaleFactor: 1.1,
                    minNeighbors: 2,
                    flags: HaarDetectionTypes.DoRoughSearch | HaarDetectionTypes.ScaleImage,
                    minSize: new OpenCvSharp.Size(30, 30)
                );
                return faces;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + "CheckFace");
            }
            return null;
        }

        private Hashtable CheckFace(Mat srcImg, Rect[] faces)
        {
            Hashtable ht = new Hashtable();
            try
            {
                foreach (Rect item in faces)
                {
                    Mat matFace = new Mat(srcImg, item);
                    Cv2.CvtColor(matFace, matFace, ColorConversionCodes.RGB2GRAY);
                    Cv2.Resize(matFace, matFace, new OpenCvSharp.Size(640, 480));
                    Cv2.EqualizeHist(matFace, matFace);
                    if (!ht.ContainsKey(matFace))
                    {
                        ht.Add(matFace, item);
                    }
                   // result.Add(matFace);
                }
                return ht;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return ht;
            }
        }

        public Hashtable PredictFace(Hashtable ht)
        {
            Hashtable result = new Hashtable();
            try
            {
                if (this.mAllInfo == null || this.mAllInfo.Count < 1)
                    return result;


                foreach(Mat item in ht.Keys)
                {
                    int groupId = this.mFaceRecognizer.Predict(item);
                    if (groupId >= 0)
                    {
                        this.Invoke(new Action(() =>
                        {
                            string name = string.Empty;
                            name = this.mAllInfo.Where(w => w.ImageGroupID == groupId).ToList().FirstOrDefault().Name;
                            result.Add(name, ht[item]);
                        }));
                    }
                }
                return result;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return result;
            }
        }

        private void FrmRecognition_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (mRecognitionThread != null)
            {
                mRecognitionThread.Abort();
                while(mRecognitionThread.ThreadState != System.Threading.ThreadState.Aborted)
                {
                    Thread.Sleep(100);
                }
            }
        }
    }
}
